/**
* @file SFXFilter.h
* @author Max Ocklind
* @version 2010-01-11
**/


#if !defined(SFXFILTER_H)
#define SFXFILTER_H


/* includes */

#include <vector>
#include <map>

#include "ALErrorChecker.h"

#include <AL/efx.h>
//#include <AL/efx-creative.h>
//#include <AL/EFX-Util.h>
//#include <AL/xram.h>


/* types */

typedef ALuint SoundSource;
typedef ALuint EffectSlot;
typedef ALuint Effect;
typedef ALuint Filter;

// a struct of the initial properties of the sound source to be filtered
typedef struct {
	ALfloat pitch;
	ALfloat gain;
	ALfloat rolloffFactor;
} SourceProperties;


/* classes */

class SFXFilter
{
protected:
	/**
	* A pointer to the Open AL error checking class.
	*/
	ALErrorChecker* errCheck;

	/**
	* The vector of EFX effect slots used by the sound effect filter.
	*/
	std::vector<EffectSlot> mEffectSlots;

	/**
	* The vector of EFX effects used by the sound effect filter.
	*/
	std::vector<Effect> mEffects;

	/**
	* The vector of EFX filters used by the sound effect filter.
	*/
	std::vector<Filter> mFilters;

	/**
	* The map of sound sources currently being filtered and their initial properties.
	*/
	std::map<SoundSource, SourceProperties> mSourceProperties;


	/* Effects Extension function pointers */
		
	// effect function pointers
	LPALGENEFFECTS	  alGenEffects;
	LPALDELETEEFFECTS alDeleteEffects;
	LPALISEFFECT	  alIsEffect;
	LPALEFFECTI		  alEffecti;
	LPALEFFECTIV	  alEffectiv;
	LPALEFFECTF		  alEffectf;
	LPALEFFECTFV	  alEffectfv;
	LPALGETEFFECTI	  alGetEffecti;
	LPALGETEFFECTIV	  alGetEffectiv;
	LPALGETEFFECTF	  alGetEffectf;
	LPALGETEFFECTFV	  alGetEffectfv;

	// filter function pointers
	LPALGENFILTERS	  alGenFilters;
	LPALDELETEFILTERS alDeleteFilters;
	LPALISFILTER	  alIsFilter;
	LPALFILTERI		  alFilteri;
	LPALFILTERIV	  alFilteriv;
	LPALFILTERF		  alFilterf;
	LPALFILTERFV	  alFilterfv;
	LPALGETFILTERI	  alGetFilteri;
	LPALGETFILTERIV	  alGetFilteriv;
	LPALGETFILTERF	  alGetFilterf;
	LPALGETFILTERFV	  alGetFilterfv;

	// auxiliary slot function pointers
	LPALGENAUXILIARYEFFECTSLOTS	   alGenAuxiliaryEffectSlots;
	LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
	LPALISAUXILIARYEFFECTSLOT	   alIsAuxiliaryEffectSlot;
	LPALAUXILIARYEFFECTSLOTI	   alAuxiliaryEffectSloti;
	LPALAUXILIARYEFFECTSLOTIV	   alAuxiliaryEffectSlotiv;
	LPALAUXILIARYEFFECTSLOTF	   alAuxiliaryEffectSlotf;
	LPALAUXILIARYEFFECTSLOTFV	   alAuxiliaryEffectSlotfv;
	LPALGETAUXILIARYEFFECTSLOTI	   alGetAuxiliaryEffectSloti;
	LPALGETAUXILIARYEFFECTSLOTIV   alGetAuxiliaryEffectSlotiv;
	LPALGETAUXILIARYEFFECTSLOTF	   alGetAuxiliaryEffectSlotf;
	LPALGETAUXILIARYEFFECTSLOTFV   alGetAuxiliaryEffectSlotfv;

	
	/* functions */

	void mPushSourceProperties(SoundSource source);
	void mPopSoundProperties(SoundSource source);

	virtual void mFilter(SoundSource source) = 0;

	bool mCheckEFXPointers();

public:
	/**
	* A dummy defualt constructor.
	*/
	SFXFilter()
	{
		// intentionally left blank
	} /* SFXFilter() */

	/**
	* Initiates the EFX extension function pointers and generates effect slots, effects and filters
	* according to the specified values.
	*
	* @param numEffectSlots the number of EFX auxiliary effect slots to be generated
	* @param numEffects the number of EFX effects to be generated
	* @param numFilters the number of EFX filters to be generated
	*/
	SFXFilter(int numEffectSlots, int numEffects, int numFilters)
	{
		errCheck = ALErrorChecker::instancePtr();

		// get the Effect Extension function pointers
		alGenEffects	= (LPALGENEFFECTS)	  alGetProcAddress("alGenEffects");
		alDeleteEffects = (LPALDELETEEFFECTS) alGetProcAddress("alDeleteEffects");
		alIsEffect		= (LPALISEFFECT)	  alGetProcAddress("alIsEffect");
		alEffecti		= (LPALEFFECTI)		  alGetProcAddress("alEffecti");
		alEffectiv		= (LPALEFFECTIV)	  alGetProcAddress("alEffectiv");
		alEffectf		= (LPALEFFECTF)		  alGetProcAddress("alEffectf");
		alEffectfv		= (LPALEFFECTFV)	  alGetProcAddress("alEffectfv");
		alGetEffecti	= (LPALGETEFFECTI)	  alGetProcAddress("alGetEffecti");
		alGetEffectiv	= (LPALGETEFFECTIV)	  alGetProcAddress("alGetEffectiv");
		alGetEffectf	= (LPALGETEFFECTF)	  alGetProcAddress("alGetEffectf");
		alGetEffectfv	= (LPALGETEFFECTFV)	  alGetProcAddress("alGetEffectfv");
		
		alGenFilters	= (LPALGENFILTERS)	  alGetProcAddress("alGenFilters");
		alDeleteFilters = (LPALDELETEFILTERS) alGetProcAddress("alDeleteFilters");
		alIsFilter		= (LPALISFILTER)	  alGetProcAddress("alIsFilter");
		alFilteri		= (LPALFILTERI)		  alGetProcAddress("alFilteri");
		alFilteriv		= (LPALFILTERIV)	  alGetProcAddress("alFilteriv");
		alFilterf		= (LPALFILTERF)		  alGetProcAddress("alFilterf");
		alFilterfv		= (LPALFILTERFV)	  alGetProcAddress("alFilterfv");
		alGetFilteri	= (LPALGETFILTERI)	  alGetProcAddress("alGetFilteri");
		alGetFilteriv	= (LPALGETFILTERIV)	  alGetProcAddress("alGetFilteriv");
		alGetFilterf	= (LPALGETFILTERF)	  alGetProcAddress("alGetFilterf");
		alGetFilterfv	= (LPALGETFILTERFV)	  alGetProcAddress("alGetFilterfv");
		
		alGenAuxiliaryEffectSlots	 = (LPALGENAUXILIARYEFFECTSLOTS)	alGetProcAddress("alGenAuxiliaryEffectSlots");
		alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS) alGetProcAddress("alDeleteAuxiliaryEffectSlots");
		alIsAuxiliaryEffectSlot		 = (LPALISAUXILIARYEFFECTSLOT)		alGetProcAddress("alIsAuxiliaryEffectSlot");
		alAuxiliaryEffectSloti		 = (LPALAUXILIARYEFFECTSLOTI)		alGetProcAddress("alAuxiliaryEffectSloti");
		alAuxiliaryEffectSlotiv		 = (LPALAUXILIARYEFFECTSLOTIV)		alGetProcAddress("alAuxiliaryEffectSlotiv");
		alAuxiliaryEffectSlotf		 = (LPALAUXILIARYEFFECTSLOTF)		alGetProcAddress("alAuxiliaryEffectSlotf");
		alAuxiliaryEffectSlotfv		 = (LPALAUXILIARYEFFECTSLOTFV)		alGetProcAddress("alAuxiliaryEffectSlotfv");
		alGetAuxiliaryEffectSloti	 = (LPALGETAUXILIARYEFFECTSLOTI)	alGetProcAddress("alGetAuxiliaryEffectSloti");
		alGetAuxiliaryEffectSlotiv	 = (LPALGETAUXILIARYEFFECTSLOTIV)	alGetProcAddress("alGetAuxiliaryEffectSlotiv");
		alGetAuxiliaryEffectSlotf	 = (LPALGETAUXILIARYEFFECTSLOTF)	alGetProcAddress("alGetAuxiliaryEffectSlotf");
		alGetAuxiliaryEffectSlotfv	 = (LPALGETAUXILIARYEFFECTSLOTFV)	alGetProcAddress("alGetAuxiliaryEffectSlotfv");

		if (!mCheckEFXPointers())
			if (AL_DEBUG)
				printf("EFX SFXFilter error: invalid Effects Extension function pointers (SFXFilter)\n");
			else
				errCheck->throwError("EFX", "SFXFilter", "SFXFilter", "invalid Effects Extension function pointers");

		int i;
		ALuint tmp;

		// generate auxiliary effect slots
		for (i = 0; i < numEffectSlots; i++)
		{
			alGenAuxiliaryEffectSlots(1, &tmp);
			errCheck->checkALError("SFXFilter", "generate effect slots (SFXFilter)");
			mEffectSlots.push_back(tmp);
		}
		
		// generate effects
		for (i = 0; i < numEffects; i++)
		{
			alGenEffects(1, &tmp);
			errCheck->checkALError("SFXFilter", "generate effects (SFXFilter)");
			mEffects.push_back(tmp);
		}

		// generate filters
		for (i = 0; i < numFilters; i++)
		{
			alGenFilters(1, &tmp);
			errCheck->checkALError("SFXFilter", "generate filters (SFXFilter)");
			mFilters.push_back(tmp);
		}		
	} /* SFXFilter(int numEffectSlots, int numEffects, int numFilters) */

	/**
	* Deletes the EFX auxiliary effect slots, EFX effects and EFX filters.
	*/
	~SFXFilter()
	{
		int i;

		// delete auxiliary effect slots
		for (i = 0; i < mEffectSlots.size(); i++)
		{
			alDeleteAuxiliaryEffectSlots(1, &mEffectSlots[i]);
			errCheck->checkALError("SFXFilter", "delete effect slots (~SFXFilter)");
		}
		
		// delete effects
		for (i = 0; i < mEffects.size(); i++)
		{
			alDeleteEffects(1, &mEffects[i]);
			errCheck->checkALError("SFXFilter", "delete effects (~SFXFilter)");
		}

		// delete filters
		for (i = 0; i < mFilters.size(); i++)
		{
			alDeleteFilters(1, &mFilters[i]);
			errCheck->checkALError("SFXFilter", "delete filters (~SFXFilter)");
		}
	} /* ~SFXFilter() */

	virtual void enable(SoundSource source);
	virtual void disable(SoundSource source);

	//virtual SXFFilter* combineWith(SFXFilter* filter);

}; /* SFXFilter */

#endif /* SFXFILTER_H */